package org.flashamaphone.voices
{
	import flash.utils.ByteArray;
	
	import org.flashamaphone.buffer.SoundConstants;
	import org.flashamaphone.voices.waveformFactories.IWaveFactory;
	
	public class ADSRVoice extends Voice
	{
		/** voice configuration */
		protected var _adsrConfig:ADSRVoiceConfig;
		
		/** 
		 * constructor
		 */
		public function ADSRVoice(config:ADSRVoiceConfig = null, volume:Number = 1){
			if (config) {
				_adsrConfig = config;
			} else {
				_adsrConfig = new ADSRVoiceConfig();
			}
			_volume = volume;
		}
		
		/**
		 * get attack bytes
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public function attack(freq:Number, milliseconds:Number, wave:IWaveFactory = null):ByteArray {
			if (!wave) {
				wave = _adsrConfig.attackWave;
			}
			var returnBytes:ByteArray = new ByteArray();
			var numSamples:int = (milliseconds * SoundConstants.SAMPLING_RATE_MILLISECONDS);
			returnBytes = wave.cycle(freq, numSamples, _lastCycle, _volume, _adsrConfig.attackEnvelope);
			_lastCycle = wave.lastSampleIndex;
			return returnBytes;
		}
		
		/**
		 * get decay bytes
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public function decay(freq:Number, milliseconds:Number, wave:IWaveFactory = null):ByteArray {
			if (!wave) {
				wave = _adsrConfig.decayWave;
			}
			var returnBytes:ByteArray = new ByteArray();
			var numSamples:int = (milliseconds * SoundConstants.SAMPLING_RATE_MILLISECONDS);
			returnBytes = wave.cycle(freq, numSamples, _lastCycle, _volume, _adsrConfig.decayEnvelope);
			_lastCycle = wave.lastSampleIndex;
			return returnBytes;
		}
		
		/**
		 * get sustain bytes
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public function sustain(freq:Number, milliseconds:Number, wave:IWaveFactory = null):ByteArray {
			if (!wave) {
				wave = _adsrConfig.sustainWave;
			}
			var returnBytes:ByteArray = new ByteArray();
			var numSamples:int = (milliseconds * SoundConstants.SAMPLING_RATE_MILLISECONDS);
			returnBytes = wave.cycle(freq, numSamples, _lastCycle, _volume, _adsrConfig.sustainEnvelope);
			_lastCycle = wave.lastSampleIndex;
			return returnBytes;
		}
		
		/**
		 * get release bytes
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public function release(freq:Number, milliseconds:Number, wave:IWaveFactory = null):ByteArray {
			if (!wave) {
				wave = _adsrConfig.releaseWave;
			}
			var returnBytes:ByteArray = new ByteArray();
			var numSamples:int = (milliseconds * SoundConstants.SAMPLING_RATE_MILLISECONDS);
			returnBytes = wave.cycle(freq, numSamples, _lastCycle, _volume, _adsrConfig.releaseEnvelope);
			_lastCycle = wave.lastSampleIndex;
			return returnBytes;
		}
		
		/** 
		 * get attack duration
		 * 
		 * @return duration
		 */
		public function get attackDuration():int {
			return _adsrConfig.attackDuration;
		}

		/** 
		 * get decay duration
		 * 
		 * @return duration
		 */
		public function get decayDuration():int {
			return _adsrConfig.decayDuration;
		}
		
		/** 
		 * get sustain duration
		 * 
		 * @return duration
		 */
		public function get sustainDuration():int {
			return _adsrConfig.sustainDuration;
		}
		
		/** 
		 * get release duration
		 * 
		 * @return duration
		 */
		public function get releaseDuration():int {
			return _adsrConfig.releaseDuration;
		}
		
		/**
		 * get press bytes (wrap up attack and decay)
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public override function press(freq:Number, milliseconds:Number, wave:IWaveFactory = null):ByteArray {
			var returnBytes:ByteArray = this.attack(freq, milliseconds, wave);
			returnBytes.writeBytes( this.decay(freq, milliseconds, wave) );
			return returnBytes;
		}
		
		/**
		 * get hold bytes (wrap up sustain)
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public override function hold(freq:Number, milliseconds:Number, streaming:Boolean = false, wave:IWaveFactory = null):ByteArray {
			return this.sustain(freq, milliseconds, wave);
		}
		
		/**
		 * get unpress bytes (wrap up sustain)
		 * 
		 * @param frequency
		 * @param duration (milliseconds)
		 * @return sound byteArray
		 */
		public override function unpress(freq:Number, milliseconds:Number, wave:IWaveFactory = null):ByteArray {
			return this.release(freq, milliseconds, wave);
		}
		
		/** 
		 * get press duration
		 * 
		 * @return duration
		 */
		override public function get pressDuration():int {
			return this.attackDuration + this.decayDuration;
		}
		
		/** 
		 * get sustain duration
		 * 
		 * @return duration
		 */
		override public function get holdDuration():int {
			return this.sustainDuration;
		}
		
		/** 
		 * get unpress duration
		 * 
		 * @return duration
		 */
		override public function get unpressDuration():int {
			return this.releaseDuration;
		}
	}
}